#ifndef __TYPES_H__
#define __TYPES_H__

#include <boost/graph/graphviz.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <ostream>
using namespace boost;

//built-in internal property tag classes
//   struct vertex_index_t { };
//   struct vertex_index1_t { };
//   struct vertex_index2_t { };
//   struct edge_index_t { };
//   struct graph_name_t { };
//   struct vertex_name_t { };
//   struct edge_name_t { };
//   struct edge_weight_t { };
//   struct edge_weight2_t { };
//   struct edge_capacity_t { };
//   struct edge_residual_capacity_t { };
//   struct edge_reverse_t { };
//   struct vertex_distance_t { };
//   struct vertex_root_t { };
//   struct vertex_all_t { };
//   struct edge_all_t { };
//   struct graph_all_t { };
//   struct vertex_color_t { };
//   struct vertex_rank_t { };
//   struct vertex_predecessor_t { };
//   struct vertex_isomorphism_t { };
//   struct vertex_invariant_t { };
//   struct vertex_invariant1_t { };
//   struct vertex_invariant2_t { };
//   struct vertex_degree_t { };
//   struct vertex_out_degree_t { };
//   struct vertex_in_degree_t { };
//   struct vertex_discover_time_t { };

//my property tag classes
enum vertex_delay_mean_t { vertex_delay_mean };
enum vertex_delay_sd_t { vertex_delay_sd };
enum edge_delay_mean_t { edge_delay_mean };
enum edge_delay_sd_t { edge_delay_sd };
enum vertex_proc_t { vertex_proc };
enum vertex_depth_vertices_t { vertex_depth_vertices };
enum vertex_depth_time_t { vertex_depth_time };
enum vertex_delay_dist_string_t { vertex_delay_dist_string };
enum vertex_delay_dist_delays_t { vertex_delay_dist_delays };
enum vertex_delay_dist_weights_t { vertex_delay_dist_weights };
enum edge_delay_dist_string_t { edge_delay_dist_string };
enum edge_delay_dist_delays_t { edge_delay_dist_delays };
enum edge_delay_dist_weights_t { edge_delay_dist_weights };
enum edge_endtime_t { edge_endtime };
enum vertex_endtime_t { vertex_endtime };
enum vertex_cp_freq_t { vertex_cp_freq };

namespace boost {
  BOOST_INSTALL_PROPERTY(vertex, delay_mean);
  BOOST_INSTALL_PROPERTY(vertex, delay_sd);
  BOOST_INSTALL_PROPERTY(vertex, proc);
  BOOST_INSTALL_PROPERTY(vertex, depth_vertices);
  BOOST_INSTALL_PROPERTY(vertex, depth_time);
  BOOST_INSTALL_PROPERTY(vertex, delay_dist_string);
  BOOST_INSTALL_PROPERTY(vertex, delay_dist_delays);
  BOOST_INSTALL_PROPERTY(vertex, delay_dist_weights);
  BOOST_INSTALL_PROPERTY(vertex, endtime);
  BOOST_INSTALL_PROPERTY(vertex, cp_freq);
  BOOST_INSTALL_PROPERTY(edge, delay_mean);
  BOOST_INSTALL_PROPERTY(edge, delay_sd);
  BOOST_INSTALL_PROPERTY(edge, delay_dist_string);
  BOOST_INSTALL_PROPERTY(edge, delay_dist_delays);
  BOOST_INSTALL_PROPERTY(edge, delay_dist_weights);
  BOOST_INSTALL_PROPERTY(edge, endtime);
}

typedef property < vertex_name_t, std::string,
         property < vertex_color_t, int,
         property < vertex_delay_mean_t, float,
         property < vertex_delay_sd_t, float,
         property < vertex_proc_t, int,
         property < vertex_depth_vertices_t, int,
         property < vertex_depth_time_t, float,
         property < vertex_delay_dist_string_t, std::string,
         property < vertex_delay_dist_delays_t, std::vector<float>,
         property < vertex_delay_dist_weights_t, std::vector<float>,
         property < vertex_endtime_t, float,
         property < vertex_cp_freq_t, int > > > > > > > > > > > > vertex_p;
// Edge properties
typedef property < edge_delay_mean_t, float,
         property < edge_delay_sd_t, float,
         property < edge_delay_dist_string_t, std::string,
         property < edge_delay_dist_delays_t, std::vector<float>,
         property < edge_delay_dist_weights_t, std::vector<float>,
         property < edge_endtime_t, float > > > > > > edge_p;
// Graph properties
typedef property < graph_name_t, std::string > graph_p;
// adjacency_list-based type
typedef adjacency_list < vecS, vecS, bidirectionalS, //last could be directedS if we didn't want access to in_edges
vertex_p, edge_p, graph_p > graph_t;

typedef boost::graph_traits<graph_t>::vertex_descriptor Vertex;
typedef boost::graph_traits<graph_t>::edge_descriptor Edge;

template <class my_graph_t>
class my_vertex_writer {
  public:
    my_vertex_writer(my_graph_t _g) : g(_g) { }
    template <class VertexClass>
    void operator()(std::ostream& out, const VertexClass& v) const {
      out << "[v_delay_mean=" << get(vertex_delay_mean, g, v)
          << ", v_delay_sd=" << get(vertex_delay_sd, g, v)
          << ", proc=" << get(vertex_proc, g, v)
          << ", depth_v=" << get(vertex_depth_vertices, g, v)
          << ", depth_t=" << get(vertex_depth_time, g, v)
          << ", style=filled"
          << ", fillcolor=" << get(vertex_cp_freq, g, v) << ", colorscheme=bugn9"
          << ", label=\"" << get(vertex_name, g, v) << "\\nP" << get(vertex_proc, g, v)
          << " (" << get(vertex_delay_mean, g, v) << "," << get(vertex_delay_sd, g, v) << ")\""
          << "]";
    }
  private:
    my_graph_t &g;
};

template <class my_graph_t>
class my_edge_writer {
  public:
    my_edge_writer(my_graph_t _g) : g(_g) { }
    template <class EdgeClass>
    void operator()(std::ostream& out, const EdgeClass& e) const {
      out << "[e_delay_mean=" << get(edge_delay_mean, g, e) << ", e_delay_sd=" << get(edge_delay_sd, g, e) << ", cut=";
      bool cut = (get(vertex_proc, g, source(e, g)) != get(vertex_proc, g, target(e, g))); //Source and target map to different processors
      if(cut)
        out << "1";
      else
        out << "0";
      out << ",label=\"(" << get(edge_delay_mean, g, e) << "," << get(edge_delay_sd, g, e) << ")\"";
      out << "]";
    }
  private:
    my_graph_t &g;
};

template <class MeanMap, class SDMap, class ProcMap>
class delay_writer {
  public:
    delay_writer(MeanMap _mm, SDMap _sdm, ProcMap _pm) : meanmap(_mm), sdmap(_sdm), procmap(_pm) {}
    template <class VertexOrEdge>
    void operator()(std::ostream& out, const VertexOrEdge& v) const {
      out << "[v_delay_mean=" << meanmap[v] << " v_delay_sd=" << sdmap[v] << " proc=" << procmap[v] << "]";
    }
  private:
    MeanMap &meanmap;
    SDMap &sdmap;
    ProcMap &procmap;
};


template <class MeanMap, class SDMap, class ProcMap>
inline delay_writer<MeanMap, SDMap, ProcMap>
make_delay_writer(MeanMap m, SDMap sd, ProcMap pm) {
    return delay_writer<MeanMap, SDMap, ProcMap>(m, sd, pm);
}

template <class MeanMap, class SDMap>
class mean_delay_writer {
  public:
    mean_delay_writer(MeanMap _mm, SDMap _sdm) : meanmap(_mm), sdmap(_sdm) {}
    template <class VertexOrEdge>
    void operator()(std::ostream& out, const VertexOrEdge& v) const {
      out << "[label=\"(" << meanmap[v] << "," << sdmap[v] << ")\"]";
    }
  private:
    MeanMap meanmap;
    SDMap sdmap;
};


template <class MeanMap, class SDMap>
inline mean_delay_writer<MeanMap, SDMap>
make_mean_delay_writer(MeanMap m, SDMap sd) {
    return mean_delay_writer<MeanMap, SDMap>(m, sd);
}

enum sim_mapping_strategy {
  s_greedy_store,
  s_greedy,
  s_stored
};

enum mapping_strategy {
  hmetis,
  greedy,
  dynamic
};

#endif